package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.common.ErrorCode;
import com.example.demo.exception.BusinessException;
import com.example.demo.mapper.*;
import com.example.demo.myModel.entity.*;
import com.example.demo.myModel.enums.SignUpStatusEnum;
import com.example.demo.myModel.otd.signup.*;
import com.example.demo.service.SignUpService;
import com.example.demo.service.TeamService;
import com.example.demo.constant.SignUpConstant;
import com.example.demo.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import com.example.demo.service.NotificationService;

@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class SignUpServiceImpl implements SignUpService {

    @Resource
    private SignUpMapper signUpMapper;
    
    @Resource
    private CompetitionMapper competitionMapper;
    
    @Resource
    private TeamService teamService;
    
    @Resource
    private TeacherMapper teacherMapper;
    
    @Resource
    private TeamMapper teamMapper;
    
    @Resource
    private MyUserMapper myUserMapper;

    @Resource
    private NotificationService notificationService;

    @Value("${upload.path}")
    private String uploadPath;

    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    @CacheEvict(value = {"signups", "teams"}, allEntries = true)
    public void submitSignUp(TeamSignUpRequest request) {
        long startTime = System.currentTimeMillis();
        try {
            validateRequest(request);
            validateCompetition(request.getCompetitionId());
            
            Team team = createTeam(request);
            addTeamMembers(team.getTeamId(), request.getMemberIds());
            checkDuplicateSignUp(request.getCompetitionId(), team.getTeamId());
            
            String materialUrl = handleMaterial(request.getMaterial());
            createSignUpRecord(request, team, materialUrl);
            
            log.info("报名成功 - 团队ID: {}, 竞赛ID: {}", team.getTeamId(), request.getCompetitionId());
        } finally {
            monitorSignUpPerformance(startTime, "提交报名");
        }
    }

    private void validateRequest(TeamSignUpRequest request) {
        if (request == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数不能为空");
        }
        if (StringUtils.isBlank(request.getTeamName())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "团队名称不能为空");
        }
        if (request.getCompetitionId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "竞赛ID不能为空");
        }
        if (request.getTeacherId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "指导教师不能为空");
        }
    }

    @Cacheable(value = "competitions", key = "#competitionId")
    private Competition getCompetition(Integer competitionId) {
        return competitionMapper.selectById(competitionId);
    }

    private void validateCompetition(Integer competitionId) {
        Competition competition = getCompetition(competitionId);
        if (competition == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }

        LocalDateTime now = LocalDateTime.now();
        if (now.isAfter(competition.getSETime())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "报名已截止");
        }
        if (now.isBefore(competition.getBTime())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "报名未开始");
        }
    }

    private Team createTeam(TeamSignUpRequest request) {
        return teamService.createTeam(request.getTeamName(), request.getLeaderId());
    }

    private void addTeamMembers(Integer teamId, List<Long> memberIds) {
        if (memberIds != null && !memberIds.isEmpty()) {
            for (Long memberId : memberIds) {
                teamService.addTeamMember(teamId, memberId);
            }
        }
    }

    private void checkDuplicateSignUp(Integer competitionId, Integer teamId) {
        QueryWrapper<SignUp> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("competition_id", competitionId)
                   .eq("team_id", teamId);
        Long count = signUpMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该团队已报名此竞赛");
        }
    }

    private void createSignUpRecord(TeamSignUpRequest request, Team team, String materialUrl) {
        SignUp signUp = new SignUp();
        signUp.setCompetitionId(request.getCompetitionId());
        signUp.setTeacherId(request.getTeacherId());
        signUp.setTeamId(team.getTeamId());
        signUp.setContent(request.getTeamIntro());
        signUp.setState(SignUpConstant.STATUS_SUBMITTED);
        signUp.setMaterial(materialUrl);
        
        int result = signUpMapper.insert(signUp);
        if (result <= 0) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "报名失败");
        }
        
        logSignUpOperation("创建报名记录", signUp.getSignUpId(), "成功");
    }

    private String handleMaterial(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return null;
        }

        try {
            FileUtils.validateFile(file);
            String fileName = UUID.randomUUID() + getFileExtension(file.getOriginalFilename());
            
            File uploadDir = new File(uploadPath);
            if (!uploadDir.exists() && !uploadDir.mkdirs()) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "建上传目录失败");
            }
            
            File destFile = new File(uploadPath, fileName);
            file.transferTo(destFile);
            
            return "/upload/" + fileName;
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件上传失败");
        }
    }

    private String getFileExtension(String filename) {
        return filename != null && filename.contains(".") ? 
            filename.substring(filename.lastIndexOf(".")) : "";
    }

    private String formatDateTime(LocalDateTime dateTime) {
        return dateTime != null ? 
            dateTime.format(DateTimeFormatter.ofPattern(SignUpConstant.DATE_TIME_FORMAT)) : null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "signups", key = "#request.signUpId")
    public void auditSignUp(SignUpAuditRequest request) {
        if (request == null || request.getSignUpId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 1. 验证报名记录
        SignUp signUp = signUpMapper.selectById(request.getSignUpId());
        if (signUp == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "报名记录不存在");
        }

        // 2. 验证状态值
        if (!isValidState(request.getState())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的审核状态");
        }
        
        // 3. 更新状态
        signUp.setState(request.getState());
        signUp.setUpdateTime(LocalDateTime.now());
        int result = signUpMapper.updateById(signUp);
        if (result <= 0) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "审核失败");
        }
        
        // 发送通知
        Team team = teamMapper.selectById(signUp.getTeamId());
        if (team != null) {
            String message = request.getState().equals(SignUpStatusEnum.APPROVED.getValue()) ?
                String.format("您的团队报名已通过审核 - 竞赛ID: %d", signUp.getCompetitionId()) :
                String.format("您的团队报名未通过审核 - 原因: %s", request.getRemark());
            
            // 发送系统通知
            notificationService.sendSystemNotification(team.getID(), message);
            
            // 发送邮件通知
            MyUser teamLeader = myUserMapper.selectById(team.getID());
            if (teamLeader != null && StringUtils.isNotBlank(teamLeader.getEmail())) {
                String subject = "竞赛��名审核结果通知";
                notificationService.sendEmailNotification(
                    teamLeader.getEmail(),
                    subject,
                    message
                );
            }
            
            log.info("审核通知发送成功 - 团队ID: {}, 状态: {}", team.getTeamId(), request.getState());
        }
    }

    @Override
    @Cacheable(value = "signups", key = "#userId + '-' + #request.state")
    public Page<SignUpDetailResponse> pageUserSignUps(Long userId, SignUpStatusRequest request) {
        // 查询用户所在的团队
        QueryWrapper<Team> teamQuery = new QueryWrapper<>();
        teamQuery.eq("id", userId);
        Team team = teamMapper.selectOne(teamQuery);
        
        if (team == null) {
            return new Page<>();
        }
        
        // 查询报名记录
        Page<SignUp> page = new Page<>(request.getCurrent(), request.getPageSize());
        QueryWrapper<SignUp> signUpQuery = new QueryWrapper<>();
        signUpQuery.eq("team_id", team.getTeamId());
        if (request.getState() != null) {
            signUpQuery.eq("state", request.getState());
        }
        
        Page<SignUp> signUpPage = signUpMapper.selectPage(page, signUpQuery);
        return convertToDetailPage(signUpPage);
    }

    /**
     * 转换为详情页面
     */
    private Page<SignUpDetailResponse> convertToDetailPage(Page<SignUp> signUpPage) {
        Page<SignUpDetailResponse> responsePage = new Page<>();
        BeanUtils.copyProperties(signUpPage, responsePage, "records");
        
        List<SignUpDetailResponse> records = signUpPage.getRecords().stream()
            .map(this::convertToDetail)
            .collect(Collectors.toList());
            
        responsePage.setRecords(records);
        return responsePage;
    }

    /**
     * 转换为详情对象
     */
    private SignUpDetailResponse convertToDetail(SignUp signUp) {
        SignUpDetailResponse response = new SignUpDetailResponse();
        BeanUtils.copyProperties(signUp, response);
        
        // 设置竞赛名称
        Competition competition = competitionMapper.selectById(signUp.getCompetitionId());
        if (competition != null) {
            response.setCompetitionName(competition.getHeadText());
        }
        
        // 设置团队名称
        Team team = teamMapper.selectById(signUp.getTeamId());
        if (team != null) {
            response.setTeamName(team.getTeamName());
        }
        
        // 设置教师名称
        Teacher teacher = teacherMapper.selectById(signUp.getTeacherId());
        if (teacher != null) {
            response.setTeacherName(teacher.getTeacherName());
        }
        
        // 设置时间格式
        if (signUp.getCreateTime() != null) {
            response.setCreateTime(formatDateTime(signUp.getCreateTime()));
        }
        if (signUp.getUpdateTime() != null) {
            response.setUpdateTime(formatDateTime(signUp.getUpdateTime()));
        }
        
        return response;
    }

    /**
     * 验证审核状态是否有效
     */
    private boolean isValidState(Integer state) {
        return state != null && (
            state.equals(SignUpStatusEnum.APPROVED.getValue()) ||
            state.equals(SignUpStatusEnum.REJECTED.getValue())
        );
    }

    // 添加日志记录
    private void logSignUpOperation(String operation, Integer signUpId, String result) {
        log.info("[竞赛报名] 操作={}, 报名ID={}, 结果={}", operation, signUpId, result);
    }

    // 添加性能监控
    private void monitorSignUpPerformance(long startTime, String operation) {
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        if (duration > 1000) { // 如果操作耗时超过1秒，记录警告日志
            log.warn("[性能监控] 操作={}, 耗时过长: {}ms", operation, duration);
        } else {
            log.info("[性能监控] 操作={}, 耗时: {}ms", operation, duration);
        }
    }
} 